/*!
    \copyright  Copyright (c) 2023 - 2024 Qualcomm Technologies International, Ltd.
                All Rights Reserved.
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \ingroup    le_unicast_manager
    \brief      Data for a GATT connection bein tracked by the le unicast manager
*/

#if defined(INCLUDE_LE_AUDIO_UNICAST)

#include "le_unicast_manager_instance.h"
#include "le_unicast_manager_private.h"
#include "gatt_connect.h"
#include "device_properties.h"
#include <gatt.h>
#include "le_unicast_manager_utils.h"
#include "device_db_serialiser.h"
#include <focus_audio_source.h>
#include "bt_device.h"
#include <focus_audio_source.h>
#include "pacs_audio_contexts.h"

le_um_task_data_t le_unicast_taskdata;


void LeUnicastManager_InstanceInit(void)
{
    le_um_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        LeUnicastManager_InstanceReset(inst);
    }
}

void LeUnicastManager_InstanceReset(le_um_instance_t *inst)
{
    le_um_cis_t *cis;

    memset(inst, 0, sizeof(*inst));
    inst->cid = INVALID_CID;
    inst->source_state = source_state_invalid;

    ARRAY_FOREACH(cis, inst->cis)
    {
        cis->cis_handle = LE_INVALID_CIS_HANDLE;
        cis->cis_id = LE_INVALID_CIS_ID;
    }
}

void leUnicastManager_GetGenericSourceForInstance(le_um_instance_t *inst, generic_source_t *source)
{
    device_t device;
    device = GattConnect_GetBtDevice(inst->cid);

    if (LeUnicastManager_IsContextOfTypeMedia(inst->audio_context))
    {
        source->type = source_type_audio;
        source->u.audio = DeviceProperties_GetLeAudioSource(device);
    }
    else
    {
        source->type = source_type_voice;
        source->u.voice = DeviceProperties_GetLeVoiceSource(device);
    }
}

le_um_instance_t* LeUnicastManager_GetInstanceByFocus(void)
{
    le_um_instance_t *match = NULL;
    le_um_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        if (LeUnicastManager_DoesInstanceHasFocus(inst))
        {
            match = inst;
            break;
        }
    }


    return match;
}

le_um_instance_t* LeUnicastManager_InstanceGetByCid(gatt_cid_t cid)
{
    le_um_instance_t *match = NULL;
    le_um_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        if (inst->cid == cid)
        {
            match = inst;
            break;
        }
    }

    return match;
}

le_um_instance_t* LeUnicastManager_InstanceGetByVoiceSource(voice_source_t source)
{
    le_um_instance_t *match = NULL;
    le_um_instance_t *inst = NULL;
    device_t device;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        if(inst->cid != INVALID_CID)
        {
            device = GattConnect_GetBtDevice(inst->cid);

            if (DeviceProperties_GetLeVoiceSource(device) == source)
            {
                match = inst;
                break;
            }
        }
    }

    return match;
}

le_um_instance_t* LeUnicastManager_InstanceGetByAudioSource(audio_source_t source)
{
    le_um_instance_t *match = NULL;
    le_um_instance_t *inst = NULL;
    device_t device;

    if (AudioSource_IsLeUnicastSource(source)
       )
    {
        ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
        {
            if (inst->cid != INVALID_CID)
            {
                device = GattConnect_GetBtDevice(inst->cid);

                if (DeviceProperties_GetLeAudioSource(device) == source)
                {
                    match = inst;
                    break;
                }
            }
        }
    }
    return match;
}

le_um_instance_t* LeUnicastManager_InstanceGetByGenericSource(generic_source_t source)
{
    le_um_instance_t *match = NULL;

    if(source.type == source_type_audio)
    {
        match = LeUnicastManager_InstanceGetByAudioSource(source.u.audio);
    }
    else if(source.type == source_type_voice)
    {
        match = LeUnicastManager_InstanceGetByVoiceSource(source.u.voice);
    }
    
    return match;
}

le_um_instance_t* LeUnicastManager_InstanceGetByCidOrCreate(gatt_cid_t cid)
{
    device_t device;
    le_um_instance_t *inst = LeUnicastManager_InstanceGetByCid(cid);
    audio_source_t audio_src = audio_source_none;

    if (inst == NULL)
    {
        inst = LeUnicastManager_InstanceGetByCid(INVALID_CID);

        if (inst != NULL)
        {
            /* A Handset device which supports LE Audio has connected.
             * Allocate the unicast audio and voice source to it. Ensure
             * to get the device using the public address of the handset device.
             */
            device = GattConnect_GetBtDevice(cid);

            /* Device should never be NULL and LEUM instance should only be created if the device supports LEA */
            if (device != NULL && BtDevice_IsProfileSupportedForDevice(device, DEVICE_PROFILE_LE_AUDIO))
            {
                inst->cid = cid;
                PanicFalse(GattConnect_GetPublicAddrFromConnectionId(cid, &inst->addr));
                audio_src = DeviceProperties_GetLeAudioSource(device);
                
                DeviceProperties_RegisterClient(device);

                if (audio_src != audio_source_le_audio_unicast_1 && audio_src != audio_source_le_audio_unicast_2)
                {
                    /* A Unicast audio & voice source is never allocated for this device. Allocate it now */
                    LeUnicastManager_AllocateSourceToDevice(device, source_type_audio);
                    LeUnicastManager_AllocateSourceToDevice(device, source_type_voice);
                    /* Set the MRU device whenever a new instance is created */
                    leUnicastManager_SetMruFromCid(cid);
                    DeviceDbSerialiser_SerialiseDevice(device);
                }
                DEBUG_LOG("LeUnicastManager_InstanceGetByCidOrCreate, instance %p created", inst);
                (void) PacsAudioContexts_InstanceCreate(cid);
            }
            else
            {
                inst = NULL;
            }
        }
    }

    return inst;
}

le_um_instance_t* LeUnicastManager_InstanceAndCisDataGetByCisHandle(uint16 cis_handle, le_um_cis_t **retrieved_cis)
{
    le_um_instance_t *match = NULL;
    le_um_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        /* Note: there is no check that the instance is valid because at the
                 moment it is possible for the CIS disconnect to arrive after
                 the ASE(s) have all been disabled and the instance has been
                 marked as invalid. */

        le_um_cis_t *cis = NULL;

        ARRAY_FOREACH(cis, inst->cis)
        {
            if (cis->state != le_um_cis_state_free && cis->cis_handle == cis_handle)
            {
                if (retrieved_cis != NULL)
                {
                    *retrieved_cis = cis;
                }
                match = inst;
                break;
            }
        }
    }

    return match;
}

le_um_instance_t* LeUnicastManager_InstanceGetByCisHandle(uint16 cis_handle)
{
    return LeUnicastManager_InstanceAndCisDataGetByCisHandle(cis_handle, NULL);
}

le_um_instance_t* LeUnicastManager_InstanceGetByAseCisId(uint8 cis_id)
{
    le_um_instance_t *match = NULL;
    le_um_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        if (LeUnicastManager_InstanceIsValid(inst))
        {
            le_um_ase_t *ase = NULL;

            ARRAY_FOREACH(ase, inst->ase)
            {
                if (   leUnicastManager_IsAseActive(ase)
                    && ase->qos_info->cisId == cis_id)
                {
                    match = inst;
                    break;
                }
            }
        }
    }
    return match;
}

audio_source_t LeUnicastManager_GetAudioSourceForInstance(le_um_instance_t *inst)
{
    device_t device;
    audio_source_t audio_source = audio_source_none;

    if (inst->cid != INVALID_CID)
    {
        device = GattConnect_GetBtDevice(inst->cid);
        audio_source = (device != NULL) ? DeviceProperties_GetLeAudioSource(device)
                                        : audio_source_none;
    }

    return audio_source;
}

voice_source_t LeUnicastManager_GetVoiceSourceForInstance(le_um_instance_t *inst)
{
    device_t device;
    voice_source_t voice_source = voice_source_none;

    if (inst->cid != INVALID_CID)
    {
        device = GattConnect_GetBtDevice(inst->cid);
        voice_source = (device != NULL) ? DeviceProperties_GetLeVoiceSource(device)
                                        : voice_source_none;
    }

    return voice_source;
}

le_um_internal_msg_t LeUnicastManager_GetCisLinklossMessageForInst(le_um_instance_t *inst)
{
    le_um_instance_t *le_um_inst = LeUnicastManager_GetTaskData()->le_unicast_instances;
    le_um_internal_msg_t msg = LE_UM_INTERNAL_INVALID_MSG;

    if (inst == &le_um_inst[0])
    {
        msg = LE_UM_INTERNAL_CIS_LINKLOSS_CONFIRMATION_INST1;
    }
    else if (inst == &le_um_inst[1])
    {
        msg = LE_UM_INTERNAL_CIS_LINKLOSS_CONFIRMATION_INST2;
    }

    return msg;
}

void LeUnicastManager_DestroyInstance(le_um_instance_t *inst)
{
    device_t device = BtDevice_GetDeviceForBdAddr(&inst->addr);

    PanicFalse(device != NULL);
    PacsAudioContexts_DestroyInstanceForCid(inst->cid);
    LeUnicastManager_RemoveSourceFromDevice(device, source_type_audio);
    LeUnicastManager_RemoveSourceFromDevice(device, source_type_voice);
    DeviceDbSerialiser_SerialiseDevice(device);
    LeUnicastManager_InstanceReset(inst);
    DeviceProperties_UnregisterClient(device);
}

uint8_t LeUnicastManager_GetNonIdleInstanceCount(void)
{
    le_um_instance_t *inst = NULL;
    uint8 active_inst_count = 0;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        if (LeUnicastManager_InstanceIsValid(inst))
        {
            le_um_ase_t *ase = NULL;

            ARRAY_FOREACH(ase, inst->ase)
            {
                if (ase->state != le_um_ase_state_idle)
                {
                    active_inst_count++;
                    break;
                }
            }
        }
    }

    return active_inst_count;
}

le_um_instance_t* LeUnicastManager_GetOtherInstance(le_um_instance_t *this_inst)
{
    le_um_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        if (inst != this_inst && LeUnicastManager_InstanceIsValid(inst))
        {
            return inst;
        }
    }

    return NULL;
}

le_um_instance_t* LeUnicastManager_GetInstanceByBdaddr(bdaddr *addr)
{
    le_um_instance_t *match = NULL;
    le_um_instance_t *inst = NULL;

    ARRAY_FOREACH(inst, LeUnicastManager_GetTaskData()->le_unicast_instances)
    {
        if (BdaddrIsSame(addr, &inst->addr))
        {
            match = inst;
            break;
        }
    }

    return match;
}

void LeUnicastManager_UpdateInstanceAudioContext(le_um_instance_t *inst, uint16 audio_context)
{
    inst->audio_context = (audio_context & AUDIO_CONTEXT_TYPE_COVERSATIONAL) ? AUDIO_CONTEXT_TYPE_COVERSATIONAL : audio_context ;
}


#endif /* INCLUDE_LE_AUDIO_UNICAST */
